#include <linux/module.h>
#include <linux/crypto.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/scatterlist.h>
#include <linux/slab.h>
#include <linux/highmem.h>

static void printkHex(char *data, int data_len, int padding_len, char* pt_mark)
{	
	int i = 0;
	printk("[%s]length=%d:%d;Data Content:\n", pt_mark, data_len, padding_len);
	for (i = 0; i < (data_len+padding_len); i ++) 
	{
		if(0 == (i%16) && i != 0)
			printk("[%d]\n",i/16);
		printk("%02x ", data[i] & 0xFF);
	}
	printk("\n");
}
static void my_test_cipher(void) //实现函数
{
	unsigned int ret;
	int i;
	char code[17] = "1234567887654321";
	char *key = "0123456789abcdef";//关键key
	char *iv = "1234567887654321";//设置iv值
	struct scatterlist sgd;//散集序列，输出
	struct scatterlist sgs;//散集序列，输入
	struct scatterlist sgl;//散集序列，解密后输出
	char last_mem[17];
	char dst_mem[17];
	char *out = NULL;
	char *result = NULL;
	char *src = NULL;
	struct crypto_blkcipher *tfm;
	struct blkcipher_desc desc;
	memset(last_mem,0,17);
	memset(dst_mem,0,17);
	/*分配块加密上下文
	cbc(aes)表示模式，也可以ofb(aes)等，通常第一个参数为0，第三个参数表示加密模式,
	*/
	tfm = crypto_alloc_blkcipher("cbc(aes)", 0, CRYPTO_ALG_ASYNC);	desc.tfm = tfm;
	desc.flags = 0;
	/*设置散集序列，将Linux内核中虚拟内存的数据传送到散集序列供dma应用，其中dst_mem，code,last_mem是我们自己设定的值，后面的16表示数据长度大小*/
	sg_init_one(&sgd,dst_mem,16);
	sg_init_one(&sgs,code,16);
	sg_init_one(&sgl,last_mem,16);
	crypto_blkcipher_setkey(tfm,key,16);//设置key
	crypto_blkcipher_set_iv(tfm,iv,16);//设置iv
	/*将sgs(散集序列，物理内存)映射到Linux内核的虚拟内存中，目的是我们可以显示其数据*/
	src = kmap(sg_page(&sgs))+sgs.offset;
	printkHex(src, sgs.length, 0, "the orginal data is");
	kunmap(sg_page(&sgs));
	//加密
	ret = crypto_blkcipher_encrypt(&desc,&sgd,&sgs,sgs.length);
	if (!ret) 
	{
		printk("AES encrypt success*************************\n");
		out = kmap(sg_page(&sgd))+sgd.offset;
		printkHex(out, sgd.length, 0, "aes encrypt");
		kunmap(sg_page(&sgd));
	}
	else
	{
		printk("the encrypt is wrong\n");
		return ;
	}
	crypto_blkcipher_setkey(tfm,key,16);
	crypto_blkcipher_set_iv(tfm,iv,16);
	//解密
	ret = crypto_blkcipher_decrypt(&desc,&sgl,&sgd,sgd.length);
	if(!ret)
	{
		printk(KERN_INFO"AES decrypt success*************************\n"); 
		result = kmap(sg_page(&sgl))+sgl.offset;
		printkHex(result, sgl.length, 0, "aes decrypt");
		kunmap(sg_page(&sgl));
	}
	else
	{
		printk("the decrypt is wrong\n");
		return;
	}
	crypto_free_blkcipher(tfm); 
}
 
static int __init aes_mod_init(void)
{
	printk("**************************aes_mod_init**************************************\n\n");
  	my_test_cipher();
	return 0;
}
static void __exit aes_mod_fini(void) 
{
	printk("\n**************************aes_mod_fini**************************************\n");
}
module_init(aes_mod_init);
module_exit(aes_mod_fini);
 
 
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Quick & dirty crypto testing module");
MODULE_AUTHOR("dachuan");
// from http://blog.csdn.net/fengjingge/article/details/42192151

